#include <iostream>
using namespace std;


// 内置类型之间
// 1、隐式类型转换    整型之间/整型和浮点数之间
// 2、显示类型的转换  指针和整型、指针之间

//int main()
//{
//	int i = 1;
//	// 隐式类型转换
//	double d = i;
//	printf("%d, %.2f\n", i, d);
//
//	int* p = &i;
//	// 显示的强制类型转换
//	int address = (int)p;
//	printf("%p, %d\n", p, address);
//
//	return 0;
//}
//// b、内置类型和自定义类型之间
//// 1、自定义类型 = 内置类型 -> 构造函数支持
//// 2、内置类型 = 自定义类型
//class A
//{
//public:
//	// 如果不想让隐式类转转换发生，可以考虑前面加explicit
//	// 但是强转是可以的，只是不支持隐式类转转换，而不是不
//	// 支持转换
//	//explicit A(int a)
//	A(int a)
//		:_a1(a)
//		,_a2(a)
//	{}
//	
//	A(int a1, int a2)
//		:_a1(a1)
//		, _a2(a2)
//	{}
//
//	// ()被仿函数占用，不能用
//	// operator 类型实现，无返回值,而不是真的没返回值
//	// 可以把自定义类型转换成int,认为返回值是int，
//	operator int()
//	{
//		return _a1 + _a2;
//	}
//private:
//	int _a1 = 1;
//	int _a2 = 1;
//};
//
//int main()
//{
//	string s1 = "11111"; // 单参数构造函数支持隐式类型转换
//	// 内置类型要转自定义类型，不能直接转，依靠构造函数进行转换
//	//A aa1 = 1;
//	A aa1 = (A)1;
//	// 多参数的隐式类型转换,中间会产生临时变量
//	A aa2 = {1,2};
//
//	//A& aa3 = { 2,2 }; // err,临时变量具有常性
//	const A& aa3 = { 2,2 };
//
//	int z = aa1.operator int(); // 本质是这样的
//	int x = aa1;
//	int y = aa2;
//	cout << x << endl;
//	cout << y << endl;
//
//	std::shared_ptr<int> foo;
//	std::shared_ptr<int> bar(new int(34));
//
//	//if (foo.operator bool())
//	 if (foo) // 本质就是调用operator bool
//		std::cout << "foo points to" << *foo << '\n';
//	else
//		std::cout << "foo is null\n";
//
//	if (bar)
//		std::cout << "bar points to" << *bar << '\n';
//	else
//		std::cout << "bar is null\n";
//
//	return 0;
//}

/*
operator int智能指针中有用这个东西,

*/

//// c、自定义类型和自定义类型之间 -- 对应的构造函数支持
//
//class A
//{
//public:
//	A(int a)
//		:_a1(a)
//		, _a2(a)
//	{}
//
//	A(int a1, int a2)
//		:_a1(a1)
//		, _a2(a2)
//	{}
//
//	int get() const
//	{
//		return _a1 + _a2;
//	}
//
//private:
//	int _a1 = 1;
//	int _a2 = 1;
//};
//class B
//{
//public:
//	B(int b)
//		:_b1(b)
//	{}
//	
//	B(const A& aa)
//		:_b1(aa.get())
//	{
//
//	}
//
//private:
//	int _b1 = 1;
//};
//
//int main()
//{
//	A aa1(1);
//	B bb1(2);
//
//	// 类型转换的特点是中间要生成一个临时对象
//	// 用aa1构造一个B的对象，在走拷贝构造
//	bb1 = aa1; 
//	B& ref1 = bb1;
//	// 不加const就引用不了，因为中间会产生临时对象
//	const B& ref2 = aa1; 
//
//
//	return 0;
//}

#include "list.h"
#include <list>
//int main()
//{
//	hao::list<int> lt = { 1,2,3,4 };
//	/*for (const auto e : lt)
//	{
//		cout << e << " ";
//	}*/
//	hao::list<int>::const_iterator cit = lt.begin();
//	while (cit != lt.end())
//	{
//		cout << *cit << " ";
//		++cit;
//	}
//	/*
//	lt调begin只能调普通的begin,不能调constbegin,调用普通
//	begin就返回的是普通迭代器，普通迭代器就给const迭代器了
//	这里不是权限的缩小和放大，权限的缩小和放大仅限于const的
//	指针和引用,而这个地方是类型转换的问题，
//	同一个类模板给不同的参数会实例化出不同的类
//
//	*/
//	return 0;
//}

//int main()
//{
//	// 对应隐式类型 -- 数据的意义没有改变，规范一点点
//	double d = 12.34;
//	int a = static_cast<int>(d);
//	cout << a << endl;
//	return 0;
//}

//int main()
//{
//	//reinterpret_cast -- 相当于强制转换 -- 数据的意义已经发生改变
//	int a = 12;
//	int* p = reinterpret_cast<int*>(a);
//	return 0;
//}

//int main()
//{
//	// 对应强制类型转换中的有风险的去掉const属性
//	// 意思就是读代码的和和写代码的人看到const_cast
//	// 就要小心了，是有风险的，
//	volatile const int a = 2;
//	int* p1 = const_cast<int*>(&a);
//	*p1 = 3;
//
//	cout << *p1 << endl;
//	cout << a << endl;
//
//	int* p2 = (int*)&a;
//	*p2 = 5;
//	cout << a << endl;
//	cout << *p2 << endl;
//	/*
//	风险就是a和*p1,*p2的值是不一样的，按理来说是一样的
//	p1,p2都是a的地址，*p1和*p2已经改了，按理来说a和*p2
//	应该是一样的。
//	原因就是编译器的优化，编译器面对const类型的变量的时候
//	有些地方会把它放到寄存器，有些地方不会放到寄存器，直接
//	就把a替换成了2，就有点像宏的样子，这个vs下面就是直接把
//	a替换成2。
//	表达的意思就是你在内存中把a修改了，但我读值的时候在寄存器
//	中读或者直接替换成常量了，所以你修改了内存我也读不到，
//	甚至在监视窗口中看a，a都修改了，但是打印出来就是2
//	*/
//	return 0;
//}

class A
{
public:
	virtual void f() {}
	int _a = 1;
};

class B : public A
{
public:
	int _b = 2;
};

void func(A* pa)
{
	/*
	父转子可以转,用static_cast、reinterpret_cast都可以,但是无论用谁
	都是不安全的,包括用强制类型转换，都是不安全的,为啥会不安全呢？
	如果这里是父类的指针，指向子类对象，转回子类是没有任何问题的，
	切割/切片，看到的是子类对象中父类的那一部分，转回子类就说明要
	多看一部分，多看一部分就是风险，如果去访问呢？
	*/
	B* pb1 = static_cast<B*>(pa);
	// B* pb2 = reinterpret_cast<B*>(pa);
	// B* pb3 = (B*)pa;
	// pa有可能指向父类对象，有可能指向子类对象，指向子类对象转是没有
	// 问题的，那指向父类对象呢？访问_a是没有问题的，访问_b就越界了
	// 越界读还行，越界写就报错了
	// 指向父类转换是有风险的，后续的访问存在越界访问的风险
	// 指向子类转换是安全的
	cout << pb1->_a << endl;
	cout << pb1->_b << endl;
	pb1->_a++;
	pb1->_b++;
	cout << pb1->_a << endl;
	cout << pb1->_b << endl;
}

//void func(A* pa)
//{
//	// dynamic_cast会先检查是否能转换成功,能成功则转换(指向子类对象),
//	// (指向父类对象是有风险的)不能则返回NULL
//	/*
//	用dynamic_cast转是安全的，dynamic_cast内部可以认为通过底层的一些
//	机制会想办法判断一下指针到底是指向父类还是指向子类。
//	*/
//	B* pb1 = dynamic_cast<B*>(pa);
//	if (pb1)
//	{
//		cout << pb1->_a << endl;
//		cout << pb1->_b << endl;
//		pb1->_a++;
//		pb1->_b++;
//		cout << pb1->_a << endl;
//		cout << pb1->_b << endl;
//	}
//	else
//	{
//		cout << "转换失败" << endl;
//	}
//}

int main()
{
	A a;
	B b;
	func(&a);
	func(&b);
	return 0;
}

/*
父类的对象是不支持转换成子类的，自定义类型之间走相关的构造也是可以的，
但是没必要这样玩，如果有特殊需求，父也是可以转子的。
父类的指针/引用转子类的指针/引用，隐式的转是不可以的，强制的转是可以的
强制类型转认为这里是不安全的，
*/

/*
dynamic_cast这个转要求是多态类型，父类需要包含虚函数，这个东西本质
是通过虚表里面加了一些标识,来处理的。指向子类就正常转换，不是就返回
空

*/
